<!-- HTML header for doxygen 1.8.6-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<title>OpenCV: Image Thresholding</title>
<link href="../../opencv.ico" rel="shortcut icon" type="image/x-icon" />
<link href="../../tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="../../jquery.js"></script>
<script type="text/javascript" src="../../dynsections.js"></script>
<script type="text/javascript" src="../../tutorial-utils.js"></script>
<link href="../../search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="../../search/searchdata.js"></script>
<script type="text/javascript" src="../../search/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js", "TeX/AMSmath.js", "TeX/AMSsymbols.js"],
    jax: ["input/TeX","output/HTML-CSS"],
});
//<![CDATA[
MathJax.Hub.Config(
{
  TeX: {
      Macros: {
          matTT: [ "\\[ \\left|\\begin{array}{ccc} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{array}\\right| \\]", 9],
          fork: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ \\end{array} \\right.", 4],
          forkthree: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ \\end{array} \\right.", 6],
          forkfour: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ #7 & \\mbox{#8}\\\\ \\end{array} \\right.", 8],
          vecthree: ["\\begin{bmatrix} #1\\\\ #2\\\\ #3 \\end{bmatrix}", 3],
          vecthreethree: ["\\begin{bmatrix} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{bmatrix}", 9],
          cameramatrix: ["#1 = \\begin{bmatrix} f_x & 0 & c_x\\\\ 0 & f_y & c_y\\\\ 0 & 0 & 1 \\end{bmatrix}", 1],
          distcoeffs: ["(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \\tau_x, \\tau_y]]]]) \\text{ of 4, 5, 8, 12 or 14 elements}"],
          distcoeffsfisheye: ["(k_1, k_2, k_3, k_4)"],
          hdotsfor: ["\\dots", 1],
          mathbbm: ["\\mathbb{#1}", 1],
          bordermatrix: ["\\matrix{#1}", 1]
      }
  }
}
);
//]]>
</script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js"></script>
<link href="../../doxygen.css" rel="stylesheet" type="text/css" />
<link href="../../stylesheet.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<!--#include virtual="/google-search.html"-->
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td id="projectlogo"><img alt="Logo" src="../../opencv-logo-small.png"/></td>
  <td style="padding-left: 0.5em;">
   <div id="projectname">OpenCV
   &#160;<span id="projectnumber">4.5.2</span>
   </div>
   <div id="projectbrief">Open Source Computer Vision</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "../../search",false,'Search');
</script>
<script type="text/javascript" src="../../menudata.js"></script>
<script type="text/javascript" src="../../menu.js"></script>
<script type="text/javascript">
$(function() {
  initMenu('../../',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<div id="nav-path" class="navpath">
  <ul>
<li class="navelem"><a class="el" href="../../d6/d00/tutorial_py_root.html">OpenCV-Python Tutorials</a></li><li class="navelem"><a class="el" href="../../d2/d96/tutorial_py_table_of_contents_imgproc.html">Image Processing in OpenCV</a></li>  </ul>
</div>
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">Image Thresholding </div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h2>Goal </h2>
<ul>
<li>In this tutorial, you will learn simple thresholding, adaptive thresholding and Otsu's thresholding.</li>
<li>You will learn the functions <b><a class="el" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57" title="Applies a fixed-level threshold to each array element. ">cv.threshold</a></b> and <b><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3" title="Applies an adaptive threshold to an array. ">cv.adaptiveThreshold</a></b>.</li>
</ul>
<h2>Simple Thresholding </h2>
<p>Here, the matter is straight-forward. For every pixel, the same threshold value is applied. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value. The function <b><a class="el" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57" title="Applies a fixed-level threshold to each array element. ">cv.threshold</a></b> is used to apply the thresholding. The first argument is the source image, which <b>should be a grayscale image</b>. The second argument is the threshold value which is used to classify the pixel values. The third argument is the maximum value which is assigned to pixel values exceeding the threshold. OpenCV provides different types of thresholding which is given by the fourth parameter of the function. Basic thresholding as described above is done by using the type <a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a147222a96556ebc1d948b372bcd7ac59" title=" ">cv.THRESH_BINARY</a>. All simple thresholding types are:</p>
<ul>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a147222a96556ebc1d948b372bcd7ac59" title=" ">cv.THRESH_BINARY</a></li>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a19120b1a11d8067576cc24f4d2f03754" title=" ">cv.THRESH_BINARY_INV</a></li>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576ac7e89a5e95490116e7d2082b3096b2b8" title=" ">cv.THRESH_TRUNC</a></li>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a0e50a338a4b711a8c48f06a6b105dd98" title=" ">cv.THRESH_TOZERO</a></li>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a47518a30aae90d799035bdcf0bb39a50" title=" ">cv.THRESH_TOZERO_INV</a></li>
</ul>
<p>See the documentation of the types for the differences.</p>
<p>The method returns two outputs. The first is the threshold that was used and the second output is the <b>thresholded image</b>.</p>
<p>This code compares the different simple thresholding types: </p><div class="fragment"><div class="line"><span class="keyword">import</span> cv2 <span class="keyword">as</span> cv</div><div class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</div><div class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> pyplot <span class="keyword">as</span> plt</div><div class="line"></div><div class="line">img = <a class="code" href="../../d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56">cv.imread</a>(<span class="stringliteral">&#39;gradient.png&#39;</span>,0)</div><div class="line">ret,thresh1 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_BINARY)</div><div class="line">ret,thresh2 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_BINARY_INV)</div><div class="line">ret,thresh3 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_TRUNC)</div><div class="line">ret,thresh4 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_TOZERO)</div><div class="line">ret,thresh5 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_TOZERO_INV)</div><div class="line"></div><div class="line">titles = [<span class="stringliteral">&#39;Original Image&#39;</span>,<span class="stringliteral">&#39;BINARY&#39;</span>,<span class="stringliteral">&#39;BINARY_INV&#39;</span>,<span class="stringliteral">&#39;TRUNC&#39;</span>,<span class="stringliteral">&#39;TOZERO&#39;</span>,<span class="stringliteral">&#39;TOZERO_INV&#39;</span>]</div><div class="line">images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]</div><div class="line"></div><div class="line"><span class="keywordflow">for</span> i <span class="keywordflow">in</span> range(6):</div><div class="line">    plt.subplot(2,3,i+1),plt.imshow(images[i],<span class="stringliteral">&#39;gray&#39;</span>,vmin=0,vmax=255)</div><div class="line">    plt.title(titles[i])</div><div class="line">    plt.xticks([]),plt.yticks([])</div><div class="line"></div><div class="line">plt.show()</div></div><!-- fragment --> <dl class="section note"><dt>Note</dt><dd>To plot multiple images, we have used the plt.subplot() function. Please checkout the matplotlib docs for more details.</dd></dl>
<p>The code yields this result:</p>
<div class="image">
<img src="../../threshold.jpg" alt="threshold.jpg"/>
<div class="caption">
image</div></div>
 <h2>Adaptive Thresholding </h2>
<p>In the previous section, we used one global value as a threshold. But this might not be good in all cases, e.g. if an image has different lighting conditions in different areas. In that case, adaptive thresholding can help. Here, the algorithm determines the threshold for a pixel based on a small region around it. So we get different thresholds for different regions of the same image which gives better results for images with varying illumination.</p>
<p>In addition to the parameters described above, the method <a class="el" href="../../d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3" title="Applies an adaptive threshold to an array. ">cv.adaptiveThreshold</a> takes three input parameters:</p>
<p>The <b>adaptiveMethod</b> decides how the threshold value is calculated:</p><ul>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa42a3e6ef26247da787bf34030ed772cad0c5199ae8637a6b195062fea4789fa9">cv.ADAPTIVE_THRESH_MEAN_C</a>: The threshold value is the mean of the neighbourhood area minus the constant <b>C</b>.</li>
<li><a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa42a3e6ef26247da787bf34030ed772caf262a01e7a3f112bbab4e8d8e28182dd">cv.ADAPTIVE_THRESH_GAUSSIAN_C</a>: The threshold value is a gaussian-weighted sum of the neighbourhood values minus the constant <b>C</b>.</li>
</ul>
<p>The <b>blockSize</b> determines the size of the neighbourhood area and <b>C</b> is a constant that is subtracted from the mean or weighted sum of the neighbourhood pixels.</p>
<p>The code below compares global thresholding and adaptive thresholding for an image with varying illumination: </p><div class="fragment"><div class="line"><span class="keyword">import</span> cv2 <span class="keyword">as</span> cv</div><div class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</div><div class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> pyplot <span class="keyword">as</span> plt</div><div class="line"></div><div class="line">img = <a class="code" href="../../d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56">cv.imread</a>(<span class="stringliteral">&#39;sudoku.png&#39;</span>,0)</div><div class="line">img = <a class="code" href="../../d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9">cv.medianBlur</a>(img,5)</div><div class="line"></div><div class="line">ret,th1 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_BINARY)</div><div class="line">th2 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3">cv.adaptiveThreshold</a>(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\</div><div class="line">            cv.THRESH_BINARY,11,2)</div><div class="line">th3 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3">cv.adaptiveThreshold</a>(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\</div><div class="line">            cv.THRESH_BINARY,11,2)</div><div class="line"></div><div class="line">titles = [<span class="stringliteral">&#39;Original Image&#39;</span>, <span class="stringliteral">&#39;Global Thresholding (v = 127)&#39;</span>,</div><div class="line">            <span class="stringliteral">&#39;Adaptive Mean Thresholding&#39;</span>, <span class="stringliteral">&#39;Adaptive Gaussian Thresholding&#39;</span>]</div><div class="line">images = [img, th1, th2, th3]</div><div class="line"></div><div class="line"><span class="keywordflow">for</span> i <span class="keywordflow">in</span> range(4):</div><div class="line">    plt.subplot(2,2,i+1),plt.imshow(images[i],<span class="stringliteral">&#39;gray&#39;</span>)</div><div class="line">    plt.title(titles[i])</div><div class="line">    plt.xticks([]),plt.yticks([])</div><div class="line">plt.show()</div></div><!-- fragment --><p> Result:</p>
<div class="image">
<img src="../../ada_threshold.jpg" alt="ada_threshold.jpg"/>
<div class="caption">
image</div></div>
 <h2>Otsu's Binarization </h2>
<p>In global thresholding, we used an arbitrary chosen value as a threshold. In contrast, Otsu's method avoids having to choose a value and determines it automatically.</p>
<p>Consider an image with only two distinct image values (<em>bimodal image</em>), where the histogram would only consist of two peaks. A good threshold would be in the middle of those two values. Similarly, Otsu's method determines an optimal global threshold value from the image histogram.</p>
<p>In order to do so, the <a class="el" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57" title="Applies a fixed-level threshold to each array element. ">cv.threshold()</a> function is used, where <a class="el" href="../../d7/d1b/group__imgproc__misc.html#ggaa9e58d2860d4afa658ef70a9b1115576a95251923e8e22f368ffa86ba8bce87ff" title="flag, use Otsu algorithm to choose the optimal threshold value ">cv.THRESH_OTSU</a> is passed as an extra flag. The threshold value can be chosen arbitrary. The algorithm then finds the optimal threshold value which is returned as the first output.</p>
<p>Check out the example below. The input image is a noisy image. In the first case, global thresholding with a value of 127 is applied. In the second case, Otsu's thresholding is applied directly. In the third case, the image is first filtered with a 5x5 gaussian kernel to remove the noise, then Otsu thresholding is applied. See how noise filtering improves the result. </p><div class="fragment"><div class="line"><span class="keyword">import</span> cv2 <span class="keyword">as</span> cv</div><div class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</div><div class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> pyplot <span class="keyword">as</span> plt</div><div class="line"></div><div class="line">img = <a class="code" href="../../d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56">cv.imread</a>(<span class="stringliteral">&#39;noisy2.png&#39;</span>,0)</div><div class="line"></div><div class="line"><span class="comment"># global thresholding</span></div><div class="line">ret1,th1 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,127,255,cv.THRESH_BINARY)</div><div class="line"></div><div class="line"><span class="comment"># Otsu&#39;s thresholding</span></div><div class="line">ret2,th2 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)</div><div class="line"></div><div class="line"><span class="comment"># Otsu&#39;s thresholding after Gaussian filtering</span></div><div class="line">blur = <a class="code" href="../../d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1">cv.GaussianBlur</a>(img,(5,5),0)</div><div class="line">ret3,th3 = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)</div><div class="line"></div><div class="line"><span class="comment"># plot all the images and their histograms</span></div><div class="line">images = [img, 0, th1,</div><div class="line">          img, 0, th2,</div><div class="line">          blur, 0, th3]</div><div class="line">titles = [<span class="stringliteral">&#39;Original Noisy Image&#39;</span>,<span class="stringliteral">&#39;Histogram&#39;</span>,<span class="stringliteral">&#39;Global Thresholding (v=127)&#39;</span>,</div><div class="line">          <span class="stringliteral">&#39;Original Noisy Image&#39;</span>,<span class="stringliteral">&#39;Histogram&#39;</span>,<span class="stringliteral">&quot;Otsu&#39;s Thresholding&quot;</span>,</div><div class="line">          <span class="stringliteral">&#39;Gaussian filtered Image&#39;</span>,<span class="stringliteral">&#39;Histogram&#39;</span>,<span class="stringliteral">&quot;Otsu&#39;s Thresholding&quot;</span>]</div><div class="line"></div><div class="line"><span class="keywordflow">for</span> i <span class="keywordflow">in</span> range(3):</div><div class="line">    plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],<span class="stringliteral">&#39;gray&#39;</span>)</div><div class="line">    plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])</div><div class="line">    plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)</div><div class="line">    plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])</div><div class="line">    plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],<span class="stringliteral">&#39;gray&#39;</span>)</div><div class="line">    plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])</div><div class="line">plt.show()</div></div><!-- fragment --><p> Result:</p>
<div class="image">
<img src="../../otsu.jpg" alt="otsu.jpg"/>
<div class="caption">
image</div></div>
 <h3>How does Otsu's Binarization work?</h3>
<p>This section demonstrates a Python implementation of Otsu's binarization to show how it actually works. If you are not interested, you can skip this.</p>
<p>Since we are working with bimodal images, Otsu's algorithm tries to find a threshold value (t) which minimizes the <b>weighted within-class variance</b> given by the relation:</p>
<p class="formulaDsp">
\[\sigma_w^2(t) = q_1(t)\sigma_1^2(t)+q_2(t)\sigma_2^2(t)\]
</p>
<p>where</p>
<p class="formulaDsp">
\[q_1(t) = \sum_{i=1}^{t} P(i) \quad \&amp; \quad q_2(t) = \sum_{i=t+1}^{I} P(i)\]
</p>
 <p class="formulaDsp">
\[\mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad \&amp; \quad \mu_2(t) = \sum_{i=t+1}^{I} \frac{iP(i)}{q_2(t)}\]
</p>
 <p class="formulaDsp">
\[\sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad \&amp; \quad \sigma_2^2(t) = \sum_{i=t+1}^{I} [i-\mu_2(t)]^2 \frac{P(i)}{q_2(t)}\]
</p>
<p>It actually finds a value of t which lies in between two peaks such that variances to both classes are minimal. It can be simply implemented in Python as follows: </p><div class="fragment"><div class="line">img = <a class="code" href="../../d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56">cv.imread</a>(<span class="stringliteral">&#39;noisy2.png&#39;</span>,0)</div><div class="line">blur = <a class="code" href="../../d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1">cv.GaussianBlur</a>(img,(5,5),0)</div><div class="line"></div><div class="line"><span class="comment"># find normalized_histogram, and its cumulative distribution function</span></div><div class="line">hist = <a class="code" href="../../d6/dc7/group__imgproc__hist.html#ga6ca1876785483836f72a77ced8ea759a">cv.calcHist</a>([blur],[0],<span class="keywordtype">None</span>,[256],[0,256])</div><div class="line">hist_norm = hist.ravel()/hist.sum()</div><div class="line">Q = hist_norm.cumsum()</div><div class="line"></div><div class="line">bins = np.arange(256)</div><div class="line"></div><div class="line">fn_min = np.inf</div><div class="line">thresh = -1</div><div class="line"></div><div class="line"><span class="keywordflow">for</span> i <span class="keywordflow">in</span> range(1,256):</div><div class="line">    p1,p2 = np.hsplit(hist_norm,[i]) <span class="comment"># probabilities</span></div><div class="line">    q1,q2 = Q[i],Q[255]-Q[i] <span class="comment"># cum sum of classes</span></div><div class="line">    <span class="keywordflow">if</span> q1 &lt; 1.e-6 <span class="keywordflow">or</span> q2 &lt; 1.e-6:</div><div class="line">        <span class="keywordflow">continue</span></div><div class="line">    b1,b2 = np.hsplit(bins,[i]) <span class="comment"># weights</span></div><div class="line"></div><div class="line">    <span class="comment"># finding means and variances</span></div><div class="line">    m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2</div><div class="line">    v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2</div><div class="line"></div><div class="line">    <span class="comment"># calculates the minimization function</span></div><div class="line">    fn = v1*q1 + v2*q2</div><div class="line">    <span class="keywordflow">if</span> fn &lt; fn_min:</div><div class="line">        fn_min = fn</div><div class="line">        thresh = i</div><div class="line"></div><div class="line"><span class="comment"># find otsu&#39;s threshold value with OpenCV function</span></div><div class="line">ret, otsu = <a class="code" href="../../d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57">cv.threshold</a>(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)</div><div class="line"><a class="code" href="../../df/d57/namespacecv_1_1dnn.html#a701210a0203f2786cbfd04b2bd56da47">print</a>( <span class="stringliteral">&quot;{} {}&quot;</span>.format(thresh,ret) )</div></div><!-- fragment --><h2>Additional Resources </h2>
<ol type="1">
<li>Digital Image Processing, Rafael C. Gonzalez</li>
</ol>
<h2>Exercises </h2>
<ol type="1">
<li>There are some optimizations available for Otsu's binarization. You can search and implement it. </li>
</ol>
</div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.6-->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Fri Apr 2 2021 11:36:37 for OpenCV by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="../../doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
<script type="text/javascript">
//<![CDATA[
addTutorialsButtons();
//]]>
</script>
</body>
</html>
